////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2005.
// -------------------------------------------------------------------------
//  File name:   FlowSaveGameNode.cpp
//  Version:     v1.00
//  Created:     28-08-2006 by AlexL
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <FlowSystem/Nodes/FlowBaseNode.h>

#include "CryAction.h"
#include "PlayerProfiles/PlayerProfileManager.h"
#include "IGame.h"
#include "ICheckPointSystem.h"

static string s_quickSave("quicksave");

class CFlowSaveGameNode : public CFlowBaseNode<eNCT_Instanced>, public IGameFrameworkListener
{
	public:

		enum 
		{
			EIP_Save = 0,
			EIP_Load,
			EIP_Name,
			EIP_Desc,
			EIP_EnableSave,
			EIP_DisableSave
		};

/*
		enum 
		{
			EOP_Saved = 0,
			EOP_Loaded
		};*/


		enum ESaveState
		{
			eSS_Idle = 0,
			eSS_Wait,
			eSS_Notify
		};

		CFlowSaveGameNode(SActivationInfo *pActInfo) : m_saveState(eSS_Idle)
		{
		}

		~CFlowSaveGameNode()
		{
			if(gEnv->pGame)
			{
				gEnv->pGame->GetIGameFramework()->UnregisterListener(this);
			}
		}

		virtual void Serialize(SActivationInfo *pActInfo, TSerialize serializer)
		{
		}

		virtual IFlowNodePtr Clone( SActivationInfo *pActInfo ) { return new CFlowSaveGameNode(pActInfo); }

		virtual void GetMemoryUsage(ICrySizer *pSizer) const
		{
			pSizer->Add(*this);
		}

		void GetConfiguration(SFlowNodeConfig &config)
		{
			static const SInputPortConfig in_config[] =
			{
				InputPortConfig_Void		("Save", _HELP("Trigger to save game")),
				InputPortConfig_Void		("Load", _HELP("Trigger to load game")),
				InputPortConfig<string>	("Name", s_quickSave, _HELP("Name of SaveGame to save/load. Use $LAST to load last savegame")),
				InputPortConfig<string>	("Desc", string(), _HELP("Description [Currently ignored]"), _HELP("Description")),
				InputPortConfig_Void		("EnableSave", _HELP("Trigger to globally allow quick-saving")),
				InputPortConfig_Void		("DisableSave", _HELP("Trigger to globally disallow quick-saving")),
				InputPortConfig_Null()
			};

			static const SOutputPortConfig out_config[] =
			{
				//OutputPortConfig_Void	("SaveDone", _HELP("Triggered when saved")),
				//OutputPortConfig_Void	("LoadDone", _HELP("Triggered when loaded")),
				OutputPortConfig_Null()
			};

			config.sDescription	= _HELP("SaveGame for Autosave");
			config.pInputPorts	= in_config;
			config.pOutputPorts	= out_config;

			config.SetCategory(EFLN_APPROVED);
		}

		void ProcessEvent(EFlowEvent event, SActivationInfo *pActInfo)
		{
			switch(event)
			{
				case eFE_Update:
				{
					if(m_saveState == eSS_Notify)
					{
						//ActivateOutput(pActInfo, EOP_Saved, true);

						if(gEnv->pGame)
						{
							gEnv->pGame->GetIGameFramework()->UnregisterListener(this);
						}

						pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, false);

						m_saveState	= eSS_Idle;

						m_name.clear();
					}

					break;
				}

				case eFE_Activate:
				{
					if(IsPortActive(pActInfo, EIP_DisableSave))
					{
						CCryAction::GetCryAction()->AllowSave(false);
					}

					if(IsPortActive(pActInfo, EIP_EnableSave))
					{
						CCryAction::GetCryAction()->AllowSave(true);
					}

					if(IsPortActive(pActInfo, EIP_Save))
					{
						string	name = GetPortString(pActInfo, EIP_Name);

						PathUtil::RemoveExtension(name);
						string path = name;
						
						path += CRY_SAVEGAME_FILE_EXT;
						CCryAction::GetCryAction()->SaveGame(path.c_str(), true, false, eSGR_FlowGraph, false, name.c_str());


						if(gEnv->IsEditor())
						{
							//ActivateOutput(pActInfo, EOP_Saved, true);
						}
						else
						{
							if(IGame *pGame = gEnv->pGame)
							{
								pGame->GetIGameFramework()->RegisterListener(this, "CFlowSaveGameNode", eFLPriority_Default);
							}

							pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, true);

							m_saveState	= eSS_Wait;
							m_name			= name;
						}
					}

					if(IsPortActive(pActInfo, EIP_Load))
					{
						string	name = GetPortString(pActInfo, EIP_Name);

						if(name == "$LAST")
						{
							CCryAction::GetCryAction()->ExecuteCommandNextFrame("loadLastSave");
						}
						else
						{
							PathUtil::RemoveExtension(name);

							name += CRY_SAVEGAME_FILE_EXT;
							const char* profile_name = CCryAction::GetCryAction()->GetIPlayerProfileManager()->GetCurrentUser();
							const char* profile = CCryAction::GetCryAction()->GetIPlayerProfileManager()->GetCurrentProfile(profile_name)->GetName();
							string path = profile + string("_") + name;

							string loadCommand = "load " + path + " 1";
							CCryAction::GetCryAction()->ExecuteCommandNextFrame(loadCommand.c_str());
						}

						//ActivateOutput(pActInfo, EOP_Loaded, true);
					}

					break;
				}
			}
		}

		// IGameFrameworkListener

		virtual void OnPostUpdate(float fDeltaTime)
		{
		}

		virtual void OnSaveGame(ISaveGame *pSaveGame)
		{
		}

		virtual void OnLoadGame(ILoadGame *pLoadGame)
		{
		}

		virtual void OnLevelEnd(const char *pNextLevel)
		{
		}

		virtual void OnActionEvent(const SActionEvent &event)
		{
			if(m_saveState == eSS_Wait && event.m_event == eAE_postSaveGame && event.m_description && !strcmp(event.m_description, m_name.c_str()))
			{
				m_saveState = eSS_Notify;
			}
		}

		// ~IGameFrameworkListener

	private:

		ESaveState	m_saveState;

		CryStackStringT<char, 128> m_name;
};

//////////////////////////////////////////////////////////////////////////

const static int NUM_SAVE_LOAD_ENTITIES = 5;

class CFlowNodeCheckpoint : public CFlowBaseNode<eNCT_Instanced>, ICheckpointListener
{
public:
	CFlowNodeCheckpoint(SActivationInfo* pActInfo)
	{
		m_iSaveId = m_iLoadId = 0;
		memset(m_saveLoadEntities, 0, sizeof(m_saveLoadEntities));
		CCryAction::GetCryAction()->GetICheckpointSystem()->RegisterListener(this);
	}

	virtual ~CFlowNodeCheckpoint()
	{
		CCryAction::GetCryAction()->GetICheckpointSystem()->RemoveListener(this);
	}

	// ICheckpointListener
	void OnSave(SCheckpointData *pCheckpoint, ICheckpointSystem *pSystem)
	{
		CRY_ASSERT(pSystem);

		//output onSave
		m_iSaveId = (int)(pCheckpoint->m_checkPointId);

		//this saves designer-controlled entities (breakables, destructables ...)
		for(int i = 0; i < NUM_SAVE_LOAD_ENTITIES; ++i)
		{
			//this comes with the ICheckpointListener
			pSystem->SaveExternalEntity(m_saveLoadEntities[i]);
		}
	}
	void OnLoad(SCheckpointData *pCheckpoint, ICheckpointSystem *pSystem)
	{
		//output onLoad
		m_iLoadId = (int)(pCheckpoint->m_checkPointId);

		//loading external entities happens inside the CheckpointSystem
	}
	//~ICheckpointListener

	virtual void Serialize(SActivationInfo* pActInfo, TSerialize ser)
	{
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}

	enum 
	{
		EIP_LoadLastCheckpoint = 0,
		EIP_SaveLoadEntityStart
	};

	enum 
	{
		EOP_OnSave = 0,
		EOP_OnLoad,
	};

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_config[] =
		{
			InputPortConfig_Void("LoadLastCheckpoint", _HELP("Load the last checkpoint which was saved during the current session.")),
			InputPortConfig<EntityId>("SaveLoadEntity1", _HELP("Save and load this entity when a checkpoint is triggered.")),
			InputPortConfig<EntityId>("SaveLoadEntity2", _HELP("Save and load this entity when a checkpoint is triggered.")),
			InputPortConfig<EntityId>("SaveLoadEntity3", _HELP("Save and load this entity when a checkpoint is triggered.")),
			InputPortConfig<EntityId>("SaveLoadEntity4", _HELP("Save and load this entity when a checkpoint is triggered.")),
			InputPortConfig<EntityId>("SaveLoadEntity5", _HELP("Save and load this entity when a checkpoint is triggered.")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_config[] = {
			OutputPortConfig<int> ("SaveDone", _HELP("Triggered when saved")),
			OutputPortConfig<int> ("LoadDone", _HELP("Triggered when loaded")),
			OutputPortConfig_Null()
		};
		config.sDescription = _HELP("Checkpoint System Output");
		config.pInputPorts = in_config;
		config.pOutputPorts = out_config;
		config.SetCategory(EFLN_APPROVED);
	}

	void ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo)
	{
		//initialization event
		switch (event)
		{
		case eFE_Initialize:
			//since we cannot send data to the flowgraph at any time, we need to wait for updates ..
			pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, true );
			break;
		case eFE_Activate:
			if (IsPortActive(pActInfo, EIP_LoadLastCheckpoint))
			{
				CCryAction::GetCryAction()->GetICheckpointSystem()->LoadLastCheckpoint();
			}
			break;
		case eFE_Update:
			if(m_iSaveId > 0)
			{
				ActivateOutput(pActInfo, EOP_OnSave, m_iSaveId);
				m_iSaveId = 0;
			}
			if(m_iLoadId > 0)
			{
				ActivateOutput(pActInfo, EOP_OnLoad, m_iLoadId);
				m_iLoadId = 0;
			}

			for(int entityIndex=0, portIndex = EIP_SaveLoadEntityStart; portIndex < (EIP_SaveLoadEntityStart+NUM_SAVE_LOAD_ENTITIES); ++portIndex, ++entityIndex)
			{
				m_saveLoadEntities[entityIndex] = GetPortEntityId(pActInfo, portIndex);


			}
/*
			m_saveLoadEntities[0] = GetPortEntityId(pActInfo, EIP_SaveLoadEntity1);
			m_saveLoadEntities[1] = GetPortEntityId(pActInfo, EIP_SaveLoadEntity2);
			m_saveLoadEntities[2] = GetPortEntityId(pActInfo, EIP_SaveLoadEntity3);
			m_saveLoadEntities[3] = GetPortEntityId(pActInfo, EIP_SaveLoadEntity4);
			m_saveLoadEntities[4] = GetPortEntityId(pActInfo, EIP_SaveLoadEntity5);
*/
			break;
		}
	}

private:

	int				m_iSaveId, m_iLoadId;
	EntityId	m_saveLoadEntities[NUM_SAVE_LOAD_ENTITIES];
};
//////////////////////////////////////////////////////////////////////////
// Register nodes

REGISTER_FLOW_NODE("System:CheckpointSystem", CFlowNodeCheckpoint);
REGISTER_FLOW_NODE( "System:SaveGame", CFlowSaveGameNode );
